package com.protocols.sctp.m3ua.sccp.tcap.camel;
import java.rmi.*;

import java.rmi.registry.*;

import java.rmi.server.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;
import java.util.TreeMap;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import java.awt.geom.GeneralPath;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.*;

import com.Main.MyServiceInterface;
import com.Main.ReceiveMessageInterface;
import com.asterisk.AmiManagerInterface;
import com.protocols.sctp.m3ua.M3uaPayLoadData;
import com.protocols.sctp.m3ua.sccp.SccpHandler;
import com.protocols.sctp.m3ua.sccp.SccpUnitDataMsg;
import com.protocols.GeneralProtocolData;
import com.protocols.ProtoOperations;
import com.protocols.sctp.TcpHandler;
import com.protocols.sctp.SctpHandlerInterface;
import com.protocols.sctp.m3ua.sccp.SccpHandlerInterface;
import com.protocols.sctp.m3ua.sccp.tcap.TcapBegin;
import com.protocols.sctp.m3ua.sccp.tcap.TcapHandlerInterface;


 

public class CamelHandler extends java.rmi.server.UnicastRemoteObject implements CamelHandlerInterface,MyServiceInterface{

	// Configuring the Logger
	
	TcapHandlerInterface TcapHandler=null;
	AmiManagerInterface AmiManager=null;
	HashMap<Long,GeneralProtocolData> CamelResponses = new HashMap<Long,GeneralProtocolData>();
	HashMap<Long,CamelSessionInfo> SessionInfo = new HashMap<Long,CamelSessionInfo>();
	String HomeGT="";
	
	private Registry Registry;  
    Logger Log = Logger.getLogger(this.getClass().getName());    
    Boolean logInfoEnabled=false;
    private Boolean amiStaus=false;
    
    
    // Default Configuration
    private int initialDpTimer=2000;
    int layerServiceport=9001;
	String tcaphandlerurl= "TcapHandler";		
	String camelhandlerurl= "CamelHandler";
	String amihandlerurl= "AmiManager";
	int ConnRetry=3000;		
	String logfile= "var/log/camel.log";
	Level loglevel= Level.OFF;
	private int MoServiceKey=20;
	private int MtServiceKey=30;
	
    
	public CamelHandler() throws RemoteException  {	
		super();
		
		
	}

    public static void main(String[] args) throws InterruptedException, IOException, NotBoundException {
		
		
			  
	
	}
	
	
	public void initialise()throws Exception {	
		
		Properties properties=null;
		try{			
			properties = new java.util.Properties();
			properties.load( new FileInputStream("General.conf"));
		}catch( IOException e){
			System.out.println("Couldn't locate the Config File");				
			System.exit(0);
		}			
		logfile= properties.getProperty("camel.log.filepath");
		loglevel= Level.parse(properties.getProperty("camel.log.level"));
		
		FileHandler filehandler=null;
		try{
			filehandler= new FileHandler(logfile,true);
			filehandler.setFormatter(new SimpleFormatter());
			Log.addHandler(filehandler);
			Log.setLevel(loglevel);				
		}catch( IOException e){
			System.out.println("Issue Opening the Log File. Setting the Log Level to 'OFF'");	
			Log.setLevel(Level.OFF);	
		}
		
		logInfoEnabled=Log.isLoggable(Level.INFO);	
		
		layerServiceport=Integer.valueOf(properties.getProperty("layerservice.port"));
		tcaphandlerurl= properties.getProperty("tcap.layerservice.url");
		camelhandlerurl= properties.getProperty("camel.layerservice.url");
		amihandlerurl= properties.getProperty("ami.layerservice.url");
		ConnRetry=Integer.valueOf(properties.getProperty("connectionretryinterval"));
		HomeGT=properties.getProperty("camel.homegt");
		amiStaus=properties.getProperty("ami.status").equals("on");
		MoServiceKey=Integer.valueOf(properties.getProperty("camel.mo.servicekey"));
		MtServiceKey=Integer.valueOf(properties.getProperty("camel.mt.servicekey"));
		initialDpTimer=Integer.valueOf(properties.getProperty("camel.idpresptimer"));
		
			
		try{
			Registry = LocateRegistry.createRegistry( layerServiceport );
			if(logInfoEnabled)Log.info("Created Registry on Port:"+layerServiceport);
		}catch(RemoteException e){
			Registry = LocateRegistry.getRegistry( layerServiceport );	
			if(logInfoEnabled)Log.info("Obtained Registry from Port:"+layerServiceport);
		}
			
		
		Registry.rebind(camelhandlerurl, this);			
		if(logInfoEnabled)Log.info("Bound Tcap Handler on Url:"+camelhandlerurl);
		
			
		
	}

	public void establishAmiConnection() throws Exception
	{
		Boolean retry=true;		
		
		while(retry)
		{
			try{
				AmiManager=(AmiManagerInterface)(Registry.lookup(amihandlerurl));
				retry=false;
			}catch(Exception e){	
				Log.severe("Couldn't Obtained the AmiManager. Retry in "+ConnRetry+" Secs."+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));
				Thread.sleep(ConnRetry);
			}
		}				
		if(logInfoEnabled)Log.info("Obtained AmiManager Instance");
	}
	
	public void establishTcapConnection() throws Exception
	{
		
		Boolean retry=true;
		while(retry)
		{
			try{
				TcapHandler=(TcapHandlerInterface)(Registry.lookup(tcaphandlerurl));
				retry=false;
			}catch(Exception e){	
				Log.severe("Couldn't Obtained the TcapHandler Instance. Retry in "+ConnRetry+" Secs."+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));
				Thread.sleep(ConnRetry);
			}
		}
		if(logInfoEnabled)Log.info("Obtained TcapHandler Instance");
	}
	@Override
	public GeneralProtocolData tacpToCamel(long sessionid, byte input [], int OPC, int DPC, String OGT, String DGT, long OrigTransactionID, long DestTransactionID) throws RemoteException{
		try{
			
			CamelSessionInfo thissessioninfo;
			synchronized (SessionInfo) {
				thissessioninfo= SessionInfo.get(DestTransactionID);
			}
			
			
			
			int count=0;
			while (input.length>0 && ++count<3){	 
				if( (input[0]&0xff)==0xa1)	// TCAP INVOKE
				{
					if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Invoke HomeGT:"+DGT+" INGT:"+OGT);
					
					CamelInvoke camel= new CamelInvoke();				
					camel.decode(input);
					
									
					if (camel.getOpCode(true)==0){	// Initial DP
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Initial DP");
						CamelInitialDP initialdp = new CamelInitialDP();
						input = initialdp.decode(input);				
						
						Registry registry=LocateRegistry.getRegistry(9001);
						registry.rebind(Long.toString(DestTransactionID), new SubscriberHandler());
						SubscriberHandlerInterface subhandler=(SubscriberHandlerInterface)(registry.lookup(Long.toString(DestTransactionID)));
						
						if(logInfoEnabled)Log.info(sessionid+":Created Subscriber Handler");
						subhandler.initialDP(sessionid, OPC, DPC, OGT, DGT, OrigTransactionID, DestTransactionID, initialdp.getCallingPartyNumber(), initialdp.getCalledNumber());
													
						
					}
					else if (camel.getOpCode(true)==23){	// Request Report BCSM
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Request Report BCSM");
						CamelRequestReportBCSMEvent requestreportbcsm = new CamelRequestReportBCSMEvent();
						input = requestreportbcsm.decode(input);
						
						if(logInfoEnabled)Log.info(sessionid+":Sending CAMEL Request Report BCSM to AMI Manager");
						// sending the BCSM events to the AMI Manager
						AmiManager.addBCSMEvents(thissessioninfo.getChannel() , requestreportbcsm);
						
					}
					else if (camel.getOpCode(true)==31){	// Continue
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Continue");
						CamelContinue camelContinue = new CamelContinue();
						input = camelContinue.decode(input);
						
						if(logInfoEnabled)Log.info(sessionid+":Updating the IN Transaction ID");
						
						synchronized (SessionInfo) {						
							if(!(SessionInfo.get(DestTransactionID)==null))SessionInfo.get(DestTransactionID).setINTransactionID(OrigTransactionID);
						}
						
						if(logInfoEnabled)Log.info(sessionid+":Addding CAMEL Continue to tyhe Response Queue");
						synchronized (CamelResponses) {						
							CamelResponses.put(DestTransactionID, camelContinue);						
						}
					} 
					else if (camel.getOpCode(true)==20){	// Connect
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Conect");
						CamelConnect camelConnect = new CamelConnect();
						input = camelConnect.decode(input);
							
						if(logInfoEnabled)Log.info(sessionid+":Updating the IN Transaction ID");
						
						synchronized (SessionInfo) {						
							if(!(SessionInfo.get(DestTransactionID)==null))SessionInfo.get(DestTransactionID).setINTransactionID(OrigTransactionID);
						}
						
						if(logInfoEnabled)Log.info(sessionid+":Addding CAMEL Connect to tyhe Response Queue");
						synchronized (CamelResponses) {						
							CamelResponses.put(DestTransactionID, camelConnect);						
						}
						
					} 
					else if (camel.getOpCode(true)==55){	// Activity Test
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Activity Test");
						CamelActvityTest camelActvityTest = new CamelActvityTest();
						input = camelActvityTest.decode(input);
						if (AmiManager.activityTest(thissessioninfo.getChannel()))
						{
							if(logInfoEnabled)Log.info(sessionid+":Activity Test Response : Channel Available");
							CamelReturnResultLast returnresult = new CamelReturnResultLast();
							returnresult.setInvokeID(camelActvityTest.getInvokeID());
							
							TcapHandler.camelToTcap(sessionid, returnresult, DGT, OGT, DestTransactionID, OrigTransactionID);
							return null;
						}
						else
						{
							if(logInfoEnabled)Log.info(sessionid+":Activity Test Response : Channel UnAvailable");
							CamelReturnResultNotLast returnresult = new CamelReturnResultNotLast();
							returnresult.setInvokeID(camelActvityTest.getInvokeID());
							TcapHandler.camelToTcap(sessionid, returnresult, DGT, OGT, DestTransactionID, OrigTransactionID);
							return null;
						}
						
					} 
					else if (camel.getOpCode(true)==35){	// Apply Charging
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Apply Charging");
						CamelApplyCharging camelApplyCharging = new CamelApplyCharging();
						input = camelApplyCharging.decode(input);	
						if(logInfoEnabled)Log.info(sessionid+":Send Apply Charging Msg to AMI");
						AmiManager.applyCharging(thissessioninfo.getChannel(), camelApplyCharging.getMaxCallPeriod(), camelApplyCharging.releaseIfDurationExceed(), camelApplyCharging.getLegId());
					} 
					else if (camel.getOpCode(true)==36){	// Apply Charging Report
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Apply Charging Report");
						CamelApplyChargingReport camelApplyChargingReport = new CamelApplyChargingReport();
						input = camelApplyChargingReport.decode(input);					
					} 
					else if (camel.getOpCode(true)==24){	// Event Report BCSM
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL Event Report BCSM");
						CamelEventReportBCSM camelEventReportBCSM = new CamelEventReportBCSM();
						input = camelEventReportBCSM.decode(input);	
						
						Registry registry=LocateRegistry.getRegistry(9001);
						SubscriberHandlerInterface subhandler=(SubscriberHandlerInterface)(registry.lookup(Long.toString(DestTransactionID)));
						subhandler.addBCSMEvents( sessionid,camelEventReportBCSM);
					} 					
					else if (camel.getOpCode(true)==22){	// Release Call
						if(logInfoEnabled)Log.info(sessionid+":Received CAMEL  Release Call");
						CamelReleaseCall camelReleaseCall = new CamelReleaseCall();
						input = camelReleaseCall.decode(input);	
						if (camelReleaseCall.getInvokeID()==81)
						{
							if(logInfoEnabled)Log.info(sessionid+":Adding the Release to the Response Queue");
							synchronized (CamelResponses) {						
								CamelResponses.put(DestTransactionID, camelReleaseCall);						
							}
						}
						else
						{
							if(logInfoEnabled)Log.info(sessionid+":Removing the Session INfo");
							
							synchronized (SessionInfo) {						
								SessionInfo.remove(DestTransactionID);						
							}
							
							if(logInfoEnabled)Log.info(sessionid+": sending the Hang up to the AMI Manager");
							AmiManager.hangUpCall(thissessioninfo.getChannel());
						}
							
					}
				}
				else if( (input[0]&0xff)==0xa2)	// TCAP Return Result
				{
					Log.info(sessionid+":Received CAMEL  Return Result");
					CamelReturnResultLast camelReturnResultLast = new CamelReturnResultLast();
					input = camelReturnResultLast.decode(input);		
					
				}
				else if( (input[0]&0xff)==0xa4)	// TCAP Reject
				{
					Log.info(sessionid+":Received CAMEL  Reject");
					CamelReject camelReject = new CamelReject();
					input = camelReject.decode(input);		
					
				}
			}
		}catch(Exception e){
			Log.warning(sessionid+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace())+ProtoOperations.getHexString(input));

		}
		return null;
	}

	@Override
	public GeneralProtocolData startInitialDP(String  channelid, String originator,String destination) throws RemoteException {
		
		long SourceTransactionID= Long.valueOf(channelid.substring(channelid.length()-8, channelid.length()),16);
		
		if(logInfoEnabled)Log.info(SourceTransactionID+": Received an INitial DP");
		
		String DGT="";
		
		//String DGT="9472000076";	//IN3	// having an issue
		
		if(originator.startsWith("94724")){
			DGT="9472000065";	//IN1
		}
		else if(originator.startsWith("94725")){
			DGT="9472000066";	//IN 2
		}
		
		
		
		try{	
			
			CamelInitialDP initialDP = new CamelInitialDP();
			initialDP.setInvokeID(1);
			initialDP.setServiceKey(MoServiceKey);
			
			initialDP.setCallingPartyNumber(0x8413, originator);
			initialDP.setCallingPartyCategory(10);
			initialDP.setBearerCap(0x8090a3);
			initialDP.setEvetTypeBCSM(2);	
			initialDP.setAgeofLocationInfo(0);
			initialDP.setVLRNumber(0x91, HomeGT);
			initialDP.setMSCNumber(0x91, HomeGT);
			initialDP.setExtBasicServiceCode(17);
			initialDP.setCallRefNumber(0x070f36b1);
			initialDP.setCalledNumber(0x81, destination);	
			initialDP.setTimeInfo(new SimpleDateFormat("yyyyMMddHHmmss").format( new Date())+"22");
			
			if(logInfoEnabled)Log.info(SourceTransactionID+": Adding Session Info");
			synchronized(SessionInfo){
				SessionInfo.put(SourceTransactionID, new CamelSessionInfo(channelid, originator, 0, 1,DGT));
			}
						
			if(logInfoEnabled)Log.info(SourceTransactionID+":Sending Initial DP to TCAP Layer Stream");
			TcapHandler.camelToTcap(SourceTransactionID, initialDP,HomeGT, DGT, SourceTransactionID,0);
					
			
			long msgsenttime=new Date().getTime();
			
			while((new Date().getTime()-msgsenttime)<initialDpTimer){
				
				GeneralProtocolData  response=null;
				if(logInfoEnabled)Log.info(SourceTransactionID+":Waiting for the Response to the IDP.");
				Thread.sleep(500);
				synchronized (CamelResponses) {					
					response= CamelResponses.remove(SourceTransactionID);			
					
				}			
				
				if (!(response==null)){						
					return response;
				}
				
				
				
			}
			
			Log.warning(SourceTransactionID+":Didn't receive any Response for the IDP winthin: "+initialDpTimer+" ms. Destination GT:"+DGT);
			
			
			
		}catch(RemoteException e){
			Log.severe(SourceTransactionID+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));

		}catch(Exception e){
			Log.severe(SourceTransactionID+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));

		}

		synchronized(SessionInfo){
			SessionInfo.remove(SourceTransactionID);
		}
		return new CamelReleaseCall();
		
	}



	@Override
	public void sendEventReportBCSM(String channelid,CamelEventReportBCSM eventReport) throws RemoteException {
		
		long SourceTransactionID= Long.valueOf(channelid.substring(channelid.length()-8, channelid.length()),16);
		if(logInfoEnabled)Log.info(SourceTransactionID+": Received BCSM Event Report");
		CamelSessionInfo thissessioninfo;
		synchronized (SessionInfo) {
			thissessioninfo= SessionInfo.get(SourceTransactionID);
		}
		if (!(thissessioninfo==null))
		{
			int nextInvokeID=thissessioninfo.getLastInvokedID()+1;
			eventReport.setInvokeID(nextInvokeID);
			thissessioninfo.setLastInvokedID(nextInvokeID);
			if(logInfoEnabled)Log.info(SourceTransactionID+": Sending BCSM Event Report to the Tcap Layer. Home GT: "+HomeGT+" INGT:"+thissessioninfo.getINGlobalTitle());
			TcapHandler.camelToTcap(SourceTransactionID, eventReport, HomeGT, thissessioninfo.getINGlobalTitle(), SourceTransactionID, thissessioninfo.getINTransactionID());
		}
	}



	@Override
	public void sendApplyChargingReport(String channelid,CamelApplyChargingReport applychargingreport)throws RemoteException {

		long SourceTransactionID= Long.valueOf(channelid.substring(channelid.length()-8, channelid.length()),16);
		if(logInfoEnabled)Log.info(SourceTransactionID+": Received Apply CHarging Report");
		CamelSessionInfo thissessioninfo;
		synchronized (SessionInfo) {
			thissessioninfo= SessionInfo.get(SourceTransactionID);
		}
		if (!(thissessioninfo==null))
		{
			int nextInvokeID=thissessioninfo.getLastInvokedID()+1;
			applychargingreport.setInvokeID(nextInvokeID);
			thissessioninfo.setLastInvokedID(nextInvokeID);
			if(logInfoEnabled)Log.info(SourceTransactionID+": Sending Apply CHarging Report to the Tcap Layer");
			TcapHandler.camelToTcap(SourceTransactionID, applychargingreport, HomeGT, thissessioninfo.getINGlobalTitle(), SourceTransactionID, thissessioninfo.getINTransactionID());
		}
		
	}

	@Override
	public String ping() throws RemoteException{
		// TODO Auto-generated method stub
		return "pong";
	}

	@Override
	public void startLayer() throws Exception {
		initialise();
		establishTcapConnection();
		if (amiStaus)establishAmiConnection();
		
	}

	@Override
	public GeneralProtocolData tacpAbort(long sessionid, int OPC, int DPC,String OGT, String DGT, long OrigTransactionID,long DestTransactionID) throws RemoteException {

		try{
			
			if(logInfoEnabled)Log.info(sessionid+":Received TCAP Abort Call");
			CamelReleaseCall camelReleaseCall = new CamelReleaseCall();
			camelReleaseCall.setInvokeID(160);
			
			if(logInfoEnabled)Log.info(sessionid+":Removing the Session INfo");
			
			CamelSessionInfo thissessioninfo=null;
			synchronized (SessionInfo) {						
				thissessioninfo=SessionInfo.remove(DestTransactionID);						
			}
			
			if(logInfoEnabled)Log.info(sessionid+": sending the Hang up to the AMI Manager");
			if(!(thissessioninfo==null))AmiManager.hangUpCall(thissessioninfo.getChannel());
			
		}catch(Exception e){
			Log.warning(sessionid+":Exception Occured :" +e.getClass().getSimpleName()+":"+ Arrays.toString(e.getStackTrace()));

		}
		return null;
	}



	

   
   

   
}